#pragma once
#ifndef Obj_h__
#define Obj_h__

#include "Common.h"
#include "Geo.h"
#include "Gfx.h"

#include <eastl/vector.h>
#include <eastl/map.h>
#include <eastl/string.h>

class DataNode
{
public:
	enum NodeType
	{
		NONE = 0,
		NUMBER,
		STRING,
		LIST,
		DICT,
	};

	typedef eastl::map<eastl::string, DataNode*> DictType;
	typedef eastl::vector<DataNode*> ListType;

	eastl::string StringVal;
	DictType Dict;
	ListType List;
	double Number;

	NodeType Type;

	//String ToString() const;

	bool IsString(const char* str) const;

	eastl::string ValueAsString(const char* default = "") const;
	double ValueAsNumber(double default = 0.0) const;

	/// TODO: Check what's faster, indexing by string or char*
	bool HasElement(const char* key) const;
	bool ElementHasStringValue(const char* key, const char* val) const;

	const DataNode* GetElement(const char* key) const;
	const DataNode* GetElementSafe(const char* key) const;

	double GetElementAsNumber(const char* key, double default = 0.0) const;
	eastl::string GetElementAsString(const char* key, const char* default = "") const;

	template <typename T>
	bool GetElementIfExists(const char* key, T* val) const
	{
		if (!val) return false;

		const DataNode* node = GetElement(key);
		if (node)
		{
			*val = (T)node->ValueAsNumber();
			return true;
		}
		return false;
	}
	template <>
	bool GetElementIfExists(const char* key, eastl::string* val) const
	{
		if (!val) return false;

		const DataNode* node = GetElement(key);
		if (node)
		{
			*val = node->ValueAsString();
			return true;
		}
		return false;
	}
	template <>
	bool GetElementIfExists(const char* key, bool* val) const
	{
		if (!val) return false;

		const DataNode* node = GetElement(key);
		if (node && node->Type == NUMBER)
		{
			*val = node->Number != 0.0;
			return true;
		}
		return false;
	}

	DataNode() : Number(0), Type(NONE) {}
	DataNode(const char* str) : StringVal(str), Number(0), Type(STRING) {}
	DataNode(const eastl::string& str) : StringVal(str), Number(0), Type(STRING) {}
	DataNode(double num) : StringVal(), Number(num), Type(NUMBER) {}
	DataNode& operator=(double num) { Destroy(); Type = NUMBER; Number = num; }
	DataNode& operator=(const DataNode& n);

	bool operator==(const eastl::string& str) const { return Type == STRING && StringVal == str; }
	bool operator==(const char* str) const { return Type == STRING && StringVal == str; }
	bool operator==(double n) const { return Type == NUMBER && Number == n; }

	operator const char*() { if (Type == STRING) return StringVal.c_str(); else return ""; }

	~DataNode() { Destroy(); }

	DataNode& operator[](const char* str);
	DataNode& operator[](const eastl::string& str) { return operator[](str.c_str()); }

	void Destroy();

	const char* Parse(const char* str); /// v
	eastl::string Encode() const;

private:

	const char* ParseDict(const char* str); /// v
	const char* ParseNumber(const char* str); /// v
	const char* ParseString(const char* str); /// v
	const char* ParseList(const char* str); /// v
	const char* ParseWordString(const char* str); /// v
	const char* StripWhitespace(const char* str); /// v

};

#endif